#include <Arduino.h>
#include "atom.h"
#include <stdio.h>
#include <pthread.h>
#include <termios.h>
#include <stdlib.h>
#define ATOM_NONE_STANDARDIO
#include "drv_serial.h"
#include "syslog.h"

#define RX_BUF_SIZE         (1024)
#define SERIAL_RX_STACK     (4096)
#define SERIAL_RX_PRI       (14)
#define SERIAL_RX_BUF_SIZE  (32)

typedef struct{
    Esp32Serial owner;
    const char *name;
    SIO_PARAM param;
    int number;
    HardwareSerial *serial;
    uint8_t rx_buf[RX_BUF_SIZE];
    uint8_t rx_stack[SERIAL_RX_STACK];
    ATOM_TCB tcb;
}Esp32SerialConfig;

static Esp32SerialConfig _config[] = {
#ifdef BSP_USE_SERIAL
    { {0}, "serial", { 115200, 8, ssb1Bit,  spNone, FALSE, FALSE, enSioBlockingSend}, 0, &Serial, {0}, {0}, DEFAULT_TCB("uart_rx") },
#endif
#ifdef BSP_USE_SERIAL1
    { {0}, "serial1", { 57600, 8, ssb1Bit,  spNone, FALSE, FALSE, enSioBlockingSend}, 1, &Serial1, {0}, {0}, DEFAULT_TCB("IAP_rx") },
#endif
#ifdef BSP_USE_SERIAL2
    { {0}, "serial2", { 115200, 8, ssb1Bit,  spNone, FALSE, FALSE, enSioBlockingSend}, 2, &Serial2, {0}, {0}, DEFAULT_TCB("GNSS_rx")},
#endif    
};

static void thread_for_serial(void* param)
{
    char key;
    Esp32SerialConfig* config = (Esp32SerialConfig*)param;
    char buffer[SERIAL_RX_BUF_SIZE];

    for(;;){
        if( config->serial->available()){
            int cnt = config->serial->read(buffer, SERIAL_RX_BUF_SIZE);
            int level = get_cc(); 
            ringPut(&config->owner.Protected.ops.rxRing, buffer, cnt);
            set_cc(level); 
        }else{
            atomTimerDelay(5);
        }
    }
}

#include "driver/uart.h"

int serial_ioctl(SioBase* sio, int function, void* arg)
{
    int ret = OK;
    Esp32Serial *serial = (Esp32Serial *)sio;
    HardwareSerial * sender = (HardwareSerial *)serial->Protected.serial;

    switch(function){
        case SIO_BAUD_SET:
            sender->updateBaudRate((int)arg);
            break;
        case SIO_BAUD_GET:
            *(uint32_t*)arg = sender->baudRate();
            break;
        case SIO_HW_OPTS_SET:
            break;
        case SIO_MODE_SET:
            sender->setMode((SerialMode)(enSioMode)(int)arg);
            break;
        case SIO_HW_PIN_SET:
        {
            SioPinSet *pins = (SioPinSet*)arg;
            sender->setPins(pins->rx, pins->tx, pins->cts, pins->rts);
            break;
        }
        default:
            ret = NG;
            break;
    }

    return ret;
}

void serial_blocking_send(SioBase* sio, const char* buffer, size_t size)
{
    Esp32Serial *serial = (Esp32Serial *)sio;
    HardwareSerial * sender = (HardwareSerial *)serial->Protected.serial;
    sender->write(buffer, size);
    sender->flush();
}

void Esp32SerialInit(void)
{
    for(int i = 0; i < ITEM_NUM(_config); i++){
        pthread_t thread;
        _config[i].serial->begin(_config[i].param.baud);
        _config[i].owner.Protected.ops.Ioctl = serial_ioctl;
        _config[i].owner.Protected.ops.BufferedSend = NULL;
        _config[i].owner.Protected.ops.BlockingSend = serial_blocking_send;
        _config[i].owner.Protected.serial = (POINTER)_config[i].serial;
        _config[i].owner.Protected.number = _config[i].number;

        SioCreate(&_config[i].owner.super, _config[i].name, &_config[i].param, &_config[i].owner.Protected.ops, (char*)_config[i].rx_buf, RX_BUF_SIZE, NULL, 0 );
        atomThreadCreate(&_config[i].tcb, SERIAL_RX_PRI, thread_for_serial, (void*)&_config[i], _config[i].rx_stack, SERIAL_RX_STACK, 0);
    }
}
